use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, sync::Arc};
use wither::{
    bson::{doc, oid::ObjectId, Bson, DateTime, Document},
    mongodb::options::{FindOneAndUpdateOptions, ReturnDocument},
    Model,
};

use crate::{
    errors::cons_error,
    mdb::{MClient, ModelEx, MDB},
    models::{
        message::{MessageHistoryModel, MessageModel, MessageReaderModel, MessageReceiver},
        user::SimpleUserModel,
    },
    AppError, AppResult, Config, Pagination, PaginationQuery,
};

use super::{club_service::ClubService, user_service::UserService};
#[allow(unused)]
#[derive(Debug, Clone)]
pub struct MessageService {
    config: Arc<Config>,
    user_service: Arc<UserService>,
    club_service: Arc<ClubService>,
    mdb: MDB,
    m_client: MClient,
}
impl MessageService {
    pub fn new(
        config: Arc<Config>,
        club_service: Arc<ClubService>,
        user_service: Arc<UserService>,
        mdb: MDB,
        m_client: MClient,
    ) -> Self {
        Self {
            config,
            club_service,
            user_service,
            mdb,
            m_client,
        }
    }

    pub async fn create_message<T: Into<CreateMessageReq>>(
        &self,
        req: T,
    ) -> AppResult<MessageModel> {
        let req: CreateMessageReq = req.into();
        let mut model = MessageModel::new(
            req.sender,
            req.receiver.clone(),
            req.title,
            req.content,
            req.extra,
        );
        model.save(&*self.mdb, None).await?;
        let model = self.process_message_children(model).await?;
        Ok(model)
    }

    /// 处理消息的子消息也就是消息回复
    pub async fn process_message_children<T: Into<MessageModel>>(
        &self,
        model: T,
    ) -> AppResult<MessageModel> {
        let model: MessageModel = model.into();
        if model.id.is_none() {
            return Ok(model);
        }
        if let MessageReceiver::Reply {
            id: ref message_id, ..
        } = model.receiver
        {
            let coll = MessageModel::collection(&*self.mdb);
            coll.find_one_and_update(
                doc! {
                    "_id":*message_id
                },
                doc! {
                    "$addToSet":{
                        "children":model.id.unwrap()
                    }
                },
                None,
            )
            .await?
            .ok_or_else(|| AppError::not_found(cons_error::MSG_MESSAGE_IS_NOT_FOUND))?;
        }
        Ok(model)
    }

    pub async fn find_all_message<T: Into<Option<Document>>>(
        &self,
        filter: T,
        query: &PaginationQuery,
    ) -> AppResult<Pagination<MessageModel>> {
        let filter: Document = {
            let m: Option<Document> = filter.into();
            m.unwrap_or_default()
        };
        MessageModel::find_and_pagination(self.mdb.clone(), filter, query).await
    }

    pub async fn find_user_receiver_message<
        U: Into<ObjectId>,
        F: Into<Option<Document>>,
        Q: Borrow<PaginationQuery>,
    >(
        &self,
        user_id: U,
        filter: F,
        query: Q,
    ) -> AppResult<Pagination<MessageModel>> {
        let user_id: ObjectId = user_id.into();
        let mut filter: Document = filter.into().unwrap_or_default();
        filter.insert(
            "$or",
            vec![
                doc! {
                    "receiver.id":user_id
                },
                doc! {
                    "receiver.user_id":user_id
                },
            ],
        );
        log::debug!("filter: {:?}", filter);
        let res =
            MessageModel::find_and_pagination(self.mdb.clone(), filter, query.borrow()).await?;
        Ok(res)
    }

    pub async fn message_detail<T: Into<ObjectId>>(&self, id: T) -> AppResult<MessageModel> {
        let oid: ObjectId = id.into();
        MessageModel::find_by_id(&*self.mdb.clone(), oid, None)
            .await?
            .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))
    }

    pub async fn delete_message<T: Into<ObjectId>>(&self, message_id: T) -> AppResult<bool> {
        let coll = MessageModel::collection(&self.mdb.clone());
        let res = coll
            .delete_one(doc! {"_id":message_id.into()}, None)
            .await?;
        Ok(res.deleted_count > 0)
    }
    pub async fn update_message<
        T: Into<ObjectId>,
        O: Into<SimpleUserModel>,
        R: Into<UpdateMessageReq>,
    >(
        &self,
        message_id: T,
        operator: O,
        req: R,
    ) -> AppResult<MessageModel> {
        let message_id: ObjectId = message_id.into();
        let operator: SimpleUserModel = operator.into();
        let req: UpdateMessageReq = req.into();
        let before = MessageModel::find_by_id(&*self.mdb.clone(), message_id, None)
            .await?
            .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        let after = MessageModel::find_one_and_update(
            &*self.mdb.clone(),
            doc! {"_id":message_id},
            doc! {"$set":req},
            FindOneAndUpdateOptions::builder()
                .return_document(ReturnDocument::After)
                .build(),
        )
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;

        let mut history = MessageHistoryModel {
            id: None,
            before: before.into(),
            after: after.clone().into(),
            operator,
            message_id,
            created_at: DateTime::now(),
        };
        history.save(&*self.mdb.clone(), None).await?;

        Ok(after)
    }

    pub async fn mark_message_to_read<T: Into<ObjectId>, U: Into<SimpleUserModel>>(
        &self,
        message_id: T,
        user: U,
    ) -> AppResult<bool> {
        let message_id: ObjectId = message_id.into();
        let user: SimpleUserModel = user.into();
        let reader = MessageReaderModel {
            id: user.id,
            nickname: user.nickname,
            avatar: user.avatar,
            created_at: DateTime::now(),
        };
        let coll = MessageModel::collection(&*self.mdb.clone());
        let res = coll
            .update_one(
                doc! {"_id":message_id,"readers.id":{"$ne":user.id}},
                doc! {"$push":{"readers":reader}},
                None,
            )
            .await?;
        Ok(res.modified_count > 0)
    }
}

// #[derive(Debug,Clone,Serialize,Deserialize)]
// pub struct CreatMessageReq {
//     #[serde(rename="type")]
//     pub message_type:MessageType,
//     pub sender:Option<SimpleUserModel>,
//     pub receiver:Option<SimpleUserModel>,
//     pub club:Option<SimpleClubModel>,
//     pub title:String,
//     pub content:String,
//     #[serde(default="Default::default")]
//     pub extra:Value,
// }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateMessageReq {
    pub sender: Option<SimpleUserModel>,
    pub receiver: MessageReceiver,
    pub title: String,
    pub content: String,
    pub extra: Option<Bson>,
}
#[derive(Debug, Clone)]
pub struct UpdateMessageReq {
    pub title: Option<String>,
    pub content: Option<String>,
    pub extra: Option<Bson>,
}

impl From<UpdateMessageReq> for Bson {
    fn from(req: UpdateMessageReq) -> Self {
        let res_doc = option_doc!(
            "title":req.title,
            "content":req.content,
            "extra":req.extra
        );
        Self::Document(res_doc)
    }
}
